import os
import subprocess
from pathlib import Path
from tree_sitter import Language, Parser, Node
from utils.gpt import GPTClient
from configuration import Config
import pathspec


class CodeAnalyzer:
    def __init__(self, language_name, config:Config,language_repo_url=None, language_so_path=None):
        """
        初始化代码分析器。

        :param language_name: 语言名称（如 "python", "java"）。
        :param language_repo_url: 语言解析库的 Git 仓库地址（可选）。
        :param language_so_path: 语言解析库的路径（可选，如果未提供则自动编译）。
        """
        self.language_name = language_name
        self.language_repo_url = language_repo_url
        self.language_so_path = language_so_path or f"build/{language_name}.so"
        self.language = self._load_or_build_language()
        self.parser = self._create_parser()
        self.config = config
        self.gpt_client = GPTClient(config=self.config)

    def _load_or_build_language(self):
        """
        加载语言解析库，如果不存在则自动编译。
        """
        if not Path(self.language_so_path).exists():
            print(f"Language library '{self.language_so_path}' not found. Building...")
            self._build_language()
        return Language(self.language_so_path, self.language_name)

    def _build_language(self):
        """
        从源码编译语言解析库。
        """
        # 确保 build 目录存在
        build_dir = Path("utils/build")
        build_dir.mkdir(exist_ok=True)

        # 克隆语言解析库的 Git 仓库
        repo_dir = build_dir / f"tree-sitter-{self.language_name}"
        if not repo_dir.exists():
            if not self.language_repo_url:
                raise ValueError(
                    f"Language repository URL for '{self.language_name}' is required."
                )
            subprocess.run(["git", "clone", self.language_repo_url, str(repo_dir)], check=True)

        # 编译语言解析库
        Language.build_library(
            self.language_so_path,
            [str(repo_dir)]
        )
        print(f"Language library '{self.language_so_path}' built successfully.")

    def _create_parser(self):
        """
        创建 Tree-sitter 解析器。
        """
        parser = Parser()
        parser.set_language(self.language)
        return parser

    def parse_code(self, code):
        """
        解析代码并返回语法树。

        :param code: 要解析的代码字符串。
        :return: 语法树的根节点。
        """
        tree = self.parser.parse(bytes(code, "utf8"))
        return tree.root_node

    def traverse_tree(self, node, callback, depth=0):
        """
        递归遍历语法树，并对每个节点执行回调函数。

        :param node: 当前节点。
        :param callback: 回调函数，接受节点和深度作为参数。
        :param depth: 当前深度（用于缩进显示）。
        """
        callback(node, depth)
        for child in node.children:
            self.traverse_tree(child, callback, depth + 1)

    def print_tree(self, node, depth=0):
        """
        打印语法树结构（用于调试）。

        :param node: 当前节点。
        :param depth: 当前深度（用于缩进显示）。
        """
        indent = "  " * depth
        print(f"{indent}{node.type} ({node.start_point} -> {node.end_point})")

    def extract_functions(self, node):
        """
        提取代码中的所有函数/方法。

        :param node: 语法树的根节点。
        :return: 函数/方法列表。
        """
        functions = []
        if node.type == "function_definition":  # Python
            function_name = node.child_by_field_name("name").text.decode("utf8")
            functions.append(function_name)
        elif node.type == "method_declaration":  # Java
            method_name = node.child_by_field_name("name").text.decode("utf8")
            functions.append(method_name)
        for child in node.children:
            functions.extend(self.extract_functions(child))
        return functions

    def extract_classes(self, node):
        """
        提取代码中的所有类。

        :param node: 语法树的根节点。
        :return: 类列表。
        """
        classes = []
        if node.type == "class_definition":  # Python
            class_name = node.child_by_field_name("name").text.decode("utf8")
            classes.append(class_name)
        elif node.type == "class_declaration":  # Java
            class_name = node.child_by_field_name("name").text.decode("utf8")
            classes.append(class_name)
        for child in node.children:
            classes.extend(self.extract_classes(child))
        return classes

    def extract_variables(self, node):
        """
        提取代码中的所有变量。

        :param node: 语法树的根节点。
        :return: 变量列表。
        """
        variables = []
        if node.type == "assignment_statement":  # Python
            variable_name = node.child_by_field_name("left").text.decode("utf8")
            variables.append(variable_name)
        elif node.type == "local_variable_declaration":  # Java
            variable_name = node.child_by_field_name("name").text.decode("utf8")
            variables.append(variable_name)
        for child in node.children:
            variables.extend(self.extract_variables(child))
        return variables

    def format_code(self, node, indent_level=0):
        """
        基于语法树重新格式化代码。

        :param node: 语法树的根节点。
        :param indent_level: 当前缩进级别。
        :return: 格式化后的代码字符串。
        """
        formatted_code = ""
        indent = "  " * indent_level

        if node.type in ["function_definition", "method_declaration"]:
            formatted_code += f"{indent}{node.text.decode('utf8')}\n"
        elif node.type in ["class_definition", "class_declaration"]:
            formatted_code += f"{indent}{node.text.decode('utf8')}\n"
        elif node.type in ["assignment_statement", "local_variable_declaration"]:
            formatted_code += f"{indent}{node.text.decode('utf8')}\n"
        else:
            for child in node.children:
                formatted_code += self.format_code(child, indent_level)

        return formatted_code

    def analyze_project(self, project_path,max_tokens=2048,detail:bool=False):
        """
        分析项目的开发语言、项目结构以及各个文件的功能和作用。

        :param project_path: 项目路径。
        :param max_tokens: 最长的输入token。
        """
        project_path = Path(project_path)
        if not project_path.exists():
            raise FileNotFoundError(f"Project path '{project_path}' does not exist.")

        # 分析项目语言
        lang=self._detect_project_language(project_path)

        # 分析项目结构
        structure=self._analyze_project_structure(project_path)

        # 分析文件功能和作用
        func_desc= self._analyze_file_functions(project_path,max_tokens)


        if not detail:
            #需要总览描述
            detail_result = self.gpt_client.call_gpt(prompt=f'''
#任务
请根据<项目使用语言>，<项目的结构>以及<每个文件的功能和作用>，总结出来这个项目的500字以内的描述
#要求
1. 项目总体描述控制在500字以内
2. 描述要能抓住重点内容
3. 尽量简洁的描述项目的功能和作用
#已知
##项目使用语言
{lang}
##项目结构
{structure}
##每个文件的功能和作用
{func_desc}
#输出格式示例
这个项目的主要是用作xxxx，功能点如下：
功能1：xxxx
功能2：xxxx
....
总之，这个项目的用途是在xxxx

''')
            return detail_result
        return lang, structure, func_desc

    def _detect_project_language(self, project_path):
        """
        检测项目的开发语言，并返回使用的语言数组。

        :param project_path: 项目根目录路径
        :return: 包含检测到的语言名称的数组
        """
        # 通过文件扩展名推断语言
        extensions = {
            ".py": "python",
            ".java": "java",
            ".js": "javascript",
            ".go": "go",
            ".rs": "rust",
            ".cpp": "cpp",
            ".c": "c",
        }

        language_counts = {}
        for file in Path(project_path).rglob("*"):
            if file.is_file():
                ext = file.suffix
                if ext in extensions:
                    language = extensions[ext]
                    language_counts[language] = language_counts.get(language, 0) + 1

        if language_counts:
            # 返回所有检测到的语言
            detected_languages = list(language_counts.keys())
            print(f"Detected project languages: {', '.join(detected_languages)}")
            return detected_languages
        else:
            print("Could not detect any project language.")
            return []

    def _analyze_project_structure(self, project_path):
        """
        分析项目结构，忽略.gitignore文件中的文件，并返回项目结构。

        :param project_path: 项目根目录路径
        :return: 嵌套字典表示的项目结构
        """
        # 读取.gitignore文件
        gitignore_path = os.path.join(project_path, '.gitignore')
        if os.path.exists(gitignore_path):
            with open(gitignore_path, 'r') as f:
                gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', f)
        else:
            gitignore_spec = None

        def build_structure(root_path):
            """
            递归构建项目结构。

            :param root_path: 当前目录路径
            :return: 当前目录的结构（字典）
            """
            structure = {}
            for name in os.listdir(root_path):
                full_path = os.path.join(root_path, name)
                # 忽略.gitignore中匹配的文件或目录
                if gitignore_spec and gitignore_spec.match_file(full_path):
                    continue

                if os.path.isdir(full_path):
                    structure[name] = build_structure(full_path)  # 递归处理子目录
                else:
                    structure[name] = None  # 文件表示为键值对，值为None
            return structure

        return build_structure(project_path)

    def _analyze_file_functions(self, project_path, max_tokens):
        """
        分析文件的功能和作用（结合AI），确保输入不超过 max_tokens 限制。

        :param project_path: 项目根目录路径
        :param max_tokens: AI 模型调用的最大 token 数限制
        :return: 代码分析结果
        """
        analysis = '代码分析:\n'
        for file in Path(project_path).rglob("*"):
            if "build" in file.parts:
                continue
            if file.is_file() and file.suffix in [".py", ".java", ".js", ".go", ".rs", ".cpp", ".c"]:
                print(f"\nAnalyzing file: {file}")
                with open(file, "r", encoding="utf-8") as f:
                    code = f.read()

                    # 将代码按 token 数分段
                    chunks = self._split_code_into_token_chunks(code, max_tokens)
                    if len(chunks) > 1:
                        print(f"File '{file}' exceeds max_tokens. Splitting into {len(chunks)} chunks for analysis...")

                    # 对每个代码段调用 AI 模型进行分析
                    for i, chunk in enumerate(chunks):
                        print(f"\nAnalyzing chunk {i + 1} of {len(chunks)}...")
                        chunk_analysis = self.gpt_client.call_gpt(prompt=f'''
# 任务
请根据以下代码片段，分析此段代码的功能以及作用。
#要求
1. 100字以内的简洁描述
# 代码片段
{chunk}
    ''')
                        analysis += f"\nAnalysis for chunk {i + 1} of {file}:\n{chunk_analysis}"
                        print(f"Analysis for chunk {i + 1}:\n{chunk_analysis}")

        return analysis

    def _split_code_into_token_chunks(self, code, max_tokens):
        """
        将代码分段，确保每段不超过最大 token 限制。

        :param code: 代码字符串。
        :param max_tokens: 每段的最大 token 数。
        :return: 分段后的代码列表。
        """
        lines = code.splitlines()
        chunks = []
        current_chunk = []

        current_length = 0
        for line in lines:
            line_length = len(line.split())  # 估算 token 数（按空格分割）
            if current_length + line_length > max_tokens:
                chunks.append("\n".join(current_chunk))
                current_chunk = []
                current_length = 0
            current_chunk.append(line)
            current_length += line_length

        if current_chunk:
            chunks.append("\n".join(current_chunk))

        return chunks

if __name__ == '__main__':
    # 初始化 Python 代码分析器
    python_analyzer = CodeAnalyzer(
        language_name="python",
        language_repo_url="https://github.com/tree-sitter/tree-sitter-python",
        config=Config()
    )

    # 示例 Python 代码
    python_code = """
    def hello():
        print("Hello, world!")

    class Greeter:
        def __init__(self):
            self.message = "Hello"

        def greet(self):
            print(self.message)
    """

    # 解析代码
    root_node = python_analyzer.parse_code(python_code)

    # 提取函数、类和变量
    functions = python_analyzer.extract_functions(root_node)
    classes = python_analyzer.extract_classes(root_node)
    variables = python_analyzer.extract_variables(root_node)

    print("Functions:", functions)
    print("Classes:", classes)
    print("Variables:", variables)

    result = python_analyzer.analyze_project('D:\code\git-change-analyzer-app')
    print(result)